#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <time.h>
// man 2 scoket
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
// man 3 inet_addr
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
// man 2 close
#include <unistd.h>
#include <pthread.h>

// 服务器的ip
#define SERVER_IP "192.168.2.77"
#define SERVER_PORT 60000

#define FILENAME "users.txt"
#define ACCOUNT_LENGTH 10
#define MAX_ATTEMPTS 100

//  用户链表结构体
struct account_info
{
    char a[ACCOUNT_LENGTH + 1];     // 账号 (字符串形式)
    char user_name[30];             // 用户名
    char password[30];              // 密码
    struct sockaddr_in client_addr; // 地址结构体
};

struct user_info
{
    int socket_fd;
    struct sockaddr_in client_addr;
};

//  通信结构体
struct message
{
    int sock_fd;             // 发送方套接字
    struct sockaddr_in addr; // 发送方地址
};

// 全局互斥锁，用于保护文件访问
pthread_mutex_t file_mutex = PTHREAD_MUTEX_INITIALIZER;

// 检查账号是否唯一
bool is_account_unique(const char *account)
{
    pthread_mutex_lock(&file_mutex); // 加锁

    FILE *fp = fopen(FILENAME, "r");
    if (!fp)
    {
        // 文件不存在，说明是第一个用户
        return true;
    }

    char line[256];
    bool unique = true;

    while (fgets(line, sizeof(line), fp))
    {
        char saved_account[ACCOUNT_LENGTH + 1];

        // 解析行首的账号 (格式: 账号,用户名,密码)
        if (sscanf(line, "%10[^,]", saved_account) == 1)
        {
            if (strcmp(saved_account, account) == 0)
            {
                unique = false;
                break;
            }
        }
    }

    fclose(fp);
    pthread_mutex_unlock(&file_mutex); // 解锁
    return unique;
}

// 生成唯一账号
bool generate_unique_account(int socket_fd, char *account_out)
{
    int attempts = 0;
    char account[ACCOUNT_LENGTH + 1] = {0};

    do
    {
        // 生成随机账号
        for (int i = 0; i < ACCOUNT_LENGTH; i++)
        {
            account[i] = '0' + rand() % 10; // 生成0-9的随机数字
        }
        account[ACCOUNT_LENGTH] = '\0'; // 字符串结束符

        // 检查唯一性
        if (is_account_unique(account))
        {
            // 发送账号给客户端
            if (send(socket_fd, account, ACCOUNT_LENGTH + 1, 0) < 0)
            {
                perror("发送账号失败");
                return false;
            }
            // 复制账号到输出参数
            strcpy(account_out, account);
            return true;
        }

        attempts++;
    } while (attempts < MAX_ATTEMPTS);

    // 发送错误消息给客户端
    const char *error_msg = "无法生成唯一账号";
    send(socket_fd, error_msg, strlen(error_msg), 0);
    return false; // 超过最大尝试次数
}

// 保存用户信息到文件
void save_user_info(const char *account, const char *username, const char *password)
{
    pthread_mutex_lock(&file_mutex); // 加锁保护文件访问

    FILE *fp = fopen(FILENAME, "a");
    if (!fp)
    {
        perror("无法打开用户文件");
        pthread_mutex_unlock(&file_mutex);
        return;
    }

    // 写入格式: 账号,用户名,密码
    fprintf(fp, "%s,%s,%s\n", account, username, password);
    fclose(fp);

    pthread_mutex_unlock(&file_mutex); // 解锁
}

// 用来监听客户端数据
void *recv_client_info(void *arg)
{
    int ret = 0;
    struct user_info cli_info = *(struct user_info *)arg;
    int socket_fd = cli_info.sock_fd;
    char *ip = inet_ntoa(cli_info.client_addr.sin_addr);
    int port = ntohs(cli_info.client_addr.sin_port);

    // 接收客户端发送的数据(recv/read)
    char buf[1024] = {0};
    char generated_account[ACCOUNT_LENGTH + 1] = {0}; // 存储生成的账号
    struct account_info acc_info;

    enum
    {
        NORMAL,
        WAITING_FOR_REG_INFO
    } state = NORMAL;

    while (1)
    {
        memset(buf, 0, sizeof(buf));

        ret = recv(socket_fd, buf, sizeof(buf) - 1, 0);
        if (ret < 0)
        {
            printf("recv fail\n");
            return NULL;
        }
        if (ret == 0)
        {
            close(socket_client);
            printf("客户端掉线[%s][%d]\n", ip, port);
            break;
        }

        buf[ret] = '\0'; // 确保字符串终止

        if (state == NORMAL)
        {
            if (strcmp(buf, "REGISTER") == 0)
            {
                printf("收到注册请求[%s][%d]\n", ip, port);
                if (!generate_unique_account(socket_fd, generated_account))
                {
                    // 生成账号失败
                    state = NORMAL;
                }
                else
                {
                    state = WAITING_FOR_REG_INFO;
                }
            }
            else
            {
                printf("[%s][%d] 收到: %s\n", ip, port, buf);
            }
        }
        else if (state == WAITING_FOR_REG_INFO)
        {
            // 确保接收完整结构体
            if (ret == sizeof(struct account_info))
            {
                memcpy(&acc_info, buf, sizeof(struct account_info));

                // 验证账号格式
                if (strlen(acc_info.a) != ACCOUNT_LENGTH)
                {
                    const char *error_msg = "无效的账号格式";
                    send(socket_fd, error_msg, strlen(error_msg), 0);
                }
                else
                {
                    // 保存用户信息
                    save_user_info(acc_info.a, acc_info.user_name, acc_info.password);

                    // 发送成功消息
                    char success_msg[100];
                    snprintf(success_msg, sizeof(success_msg), "注册成功! 账号: %s, 用户名: %s", acc_info.a, acc_info.user_name);
                    send(socket_fd, success_msg, strlen(success_msg), 0);
                }
                state = NORMAL; // 返回正常状态
            }

            else if (strncmp(buf, "LOGIN", 5) == 0)
            {
                char received_username[30] = {0};
                recv(socket_fd, received_username, sizeof(received_username), 0);

                FILE *fp = fopen(FILENAME, "r");
                if (!fp)
                {
                    const char *msg = "USER_NOT_FOUND";
                    send(socket_fd, msg, strlen(msg), 0);
                    continue;
                }

                bool found = false;
                char line[256];
                while (fgets(line, sizeof(line), fp))
                {
                    char saved_account[ACCOUNT_LENGTH + 1], saved_name[30], saved_pass[30];
                    if (sscanf(line, "%10[^,],%29[^,],%29[^\n]", saved_account, saved_name, saved_pass) == 3)
                    {
                        if (strcmp(saved_name, received_username) == 0 || strcmp(saved_account, received_username) == 0)
                        {
                            found = true;
                            const char *need_pwd = "NEED_PASSWORD";
                            send(socket_fd, need_pwd, strlen(need_pwd), 0);

                            char received_password[30] = {0};
                            recv(socket_fd, received_password, sizeof(received_password), 0);

                            if (strcmp(received_password, saved_pass) == 0)
                            {
                                const char *success = "LOGIN_SUCCESS";
                                send(socket_fd, success, strlen(success), 0);
                                break;
                            }
                            else
                            {
                                const char *fail = "LOGIN_FAIL";
                                send(socket_fd, fail, strlen(fail), 0);
                            }
                        }
                    }
                }

                if (!found)
                {
                    const char *not_found = "USER_NOT_FOUND";
                    send(socket_fd, not_found, strlen(not_found), 0);
                }

                fclose(fp);
            }
        }
    }

    close(socket_fd);
    return NULL;
}

int main(int argc, char **argv)
{
    srand(time(NULL));

    int ret = 0;

    // 创建套接字(socket)
    // ipv4地址族 tcp协议
    int socket_fd = socket(AF_INET, SOCK_STREAM, 0);
    if (socket_fd < 0)
    {
        printf("socket fail\n");
        return -1;
    }

    // 所以设置端口号可以复用,这两条语句放在 绑定bind 之前
    int optval = 1;
    setsockopt(socket_fd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval));
    // 绑定本机IP和port(bind)
    struct sockaddr_in server_addr;                     // 新的结构体
    server_addr.sin_family = AF_INET;                   // 地址族
    server_addr.sin_port = htons(SERVER_PORT);          // 将本机端口转换为网络端口(将小段字节序转换为大端字节序)
    server_addr.sin_addr.s_addr = inet_addr(SERVER_IP); // 将本机ip转换为网络ip
    ret = bind(socket_fd, (struct sockaddr *)&server_addr, sizeof(struct sockaddr_in));
    if (ret < 0)
    {
        printf("bind fail\n");
        return -1;
    }
    printf("绑定本机成功[%s][%d]\n", SERVER_IP, SERVER_PORT);

    // 监听(listen)
    ret = listen(socket_fd, 20);
    if (ret < 0)
    {
        printf("listen fail\n");
        return -1;
    }

    // 主线程用来监听客户端的上线
    //  连接客户端(accept)
    int socket_client = -1; // 用来存储客户端的套接字
    struct sockaddr_in client_addr;
    int addr_len = sizeof(struct sockaddr_in);
    while (1)
    {
        socket_client = accept(socket_fd, (struct sockaddr *)&client_addr, (socklen_t *)&addr_len);
        if (socket_client < 0)
        {
            printf("accept fail\n");
            return -1;
        }
        // 解析客户端的ip和port(需要将网络字节序转换为本机字节序)
        int port = ntohs(client_addr.sin_port);                          // 将网络端口转换为本机端口
        char *ip = inet_ntoa(client_addr.sin_addr);                      // 将网络ip转换为本机ip
        printf("新的客户端上线[%s][%d][%d]\n", ip, port, socket_client); // 可以知道谁上线了

        // 创建一个子线程用来监听客户端的数据
        struct user_info cli_info;
        cli_info.sock_fd = socket_client;
        cli_info.client_addr = client_addr;

        pthread_t tid = 0;
        ret = pthread_create(&tid, NULL, recv_client_info, (void *)&cli_info);
        if (ret != 0)
        {
            printf("pthread_create fail\n");
            return -1;
        }
    }

    // 关闭套接字(close)
    close(socket_fd);

    return 0;
}